package com.alex.demo.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.IdcardUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.core.util.URLUtil;
import cn.hutool.poi.excel.ExcelReader;
import cn.hutool.poi.excel.ExcelUtil;
import cn.hutool.poi.excel.ExcelWriter;
import cn.hutool.poi.word.Word07Writer;
import com.alex.demo.common.R;
import com.alex.demo.common.YcStation;
import com.alex.demo.mapper.LieCheMapper;
import com.alex.demo.pojo.LieChe;
import com.alex.demo.pojo.MiJiePerson;
import com.alex.demo.service.LieCheService;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.apache.commons.compress.utils.IOUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.io.*;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.*;
import java.util.stream.Collectors;

import static cn.hutool.core.io.FileUtil.getInputStream;
import static org.apache.ibatis.io.Resources.getResourceAsStream;


/**
 * <p>
 *  服务实现类
 * </p>
 *
 * @author Alex
 * @since 2022-03-26
 */
@Service
public class LieCheServiceImpl extends ServiceImpl<LieCheMapper, LieChe> implements LieCheService {

    @Autowired
    LieCheMapper lieCheMapper;

    LambdaQueryWrapper<LieChe> wrapper = new LambdaQueryWrapper<>();

    Map<String, String> arrStation = new LinkedHashMap<>();

    /**
     * 获取全部的数据库信息
     * @return 数据库人员列表
     */
    @Override
    public List<LieChe> getAll() {
        wrapper.clear();
        lieCheMapper.selectList(wrapper);
        return null;
    }

    /**
     * 返回文件下载链接
     * @param response 请求
     * @param lieChe 主体信息
     */
    @Override
    public void writerExcel(HttpServletResponse response,LieChe lieChe){

        //本次列车前后三排乘客
        List<LieChe> personList =getPersonList(lieChe);

        //获取密接人员
        List<LieChe> miJieList =getFilter(lieChe,personList);

        //生成密接人员详细信息
        List<MiJiePerson> personInfo =getPersonInfo(miJieList);

        List<MiJiePerson> rows = CollUtil.newArrayList(personInfo);

        // 通过工具类创建writer
        ExcelWriter writer = ExcelUtil.getWriter();

        writer.addHeaderAlias("name", "姓名");
        writer.addHeaderAlias("sex", "性别");
        writer.addHeaderAlias("IdCard", "身份证");
        writer.addHeaderAlias("isYan", "盐城籍");
        writer.addHeaderAlias("carriage", "车厢号");
        writer.addHeaderAlias("seatNum", "座位号");
        writer.addHeaderAlias("depart", "起始站");
        writer.addHeaderAlias("departTime", "发车时间");
        writer.addHeaderAlias("arrivedType", "盐城境内下车");
        writer.addHeaderAlias("arrived", "抵达站");
        writer.addHeaderAlias("arrivedTime", "抵达站时间");

        writer.setOnlyAlias(true);

        // 一次性写出内容，使用默认样式，强制输出标题
        writer.write(rows, true);

        ServletOutputStream out= null;

        String name = "excel";
        response.setContentType("application/octet-stream;charset=utf-8");
        response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode(name+".xlsx"));
        try {
            out = response.getOutputStream();
            writer.flush(out, true);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * 返回文件下载链接
     * @param response 请求
     * @param lieChe 主体信息
     */
    @Override
    public R writeWord(HttpServletResponse response, LieChe lieChe) {
        //本次列车所有乘客
        List<LieChe> allList =getAllList(lieChe);

        //本次所乘列车同一车厢所有乘客
        List<LieChe> carriageList =getCarriageList(lieChe);

        //本次列车前后三排乘客
        List<LieChe> personList =getPersonList(lieChe);

        //获取密接人员
        List<LieChe> miJieList =getFilter(lieChe,personList);

        //生成密接人员详细信息
        List<MiJiePerson> personInfo =getPersonInfo(miJieList);

        Word07Writer writer = new Word07Writer();

        Map<String, List<MiJiePerson>> map = getDown(personInfo);
        //统计盐城籍的人数
        int miJieCount = personInfo.size();
        int contNo = 0;

        //统计在盐城境内下车人数信息
        int conDown = 0;
        for (MiJiePerson p:personInfo) {
            if (p.getIsYan().contains("非")){
                contNo++;
            }
            if(p.getArrivedType().equals("是")){
                conDown++;
            }
        }
        int contYes = miJieCount-contNo;

        Font header = new Font("黑体", Font.BOLD, 20);
        Font font = new Font("宋体", Font.PLAIN, 16);
        // 添加段落（标题）
        writer.addText(font,allList.size()+"人中，"
                        +lieChe.getCarriage()+"号车厢共计"
                        +carriageList.size()+"人，前三排后三排共计 "
                        +personList.size()+"人，其中可能发生密接的为"
                        +miJieCount+"人");
        // 添加段落（正文）
        writer.addText(font,miJieCount+"人中，盐城籍为："
                        +contYes+"人，非盐城籍人为："
                        +contNo+"人，包含"
                        +lieChe.getName()
                        +"本人,在盐城境内下车的共计："
                        +conDown+"人");
        for (YcStation e : YcStation.values()) {
            String stationName = e.getStationName();
            List<MiJiePerson> list = map.get(stationName);
            if(null != list && list.size() !=0){
                writer.addText(header, stationName +" 下车人数为："+list.size());
                for (MiJiePerson person:list) {
                    int index = list.indexOf(person)+1;
                    writer.addText(font,index+"."+ person.toString());
                }
            }
        }
        response.setContentType("application/vnd.lotus-wordpro;charset=utf-8");
        response.setHeader("Content-Disposition","attachment;filename=word.docx");

        ServletOutputStream out= null;
        try {
            out = response.getOutputStream();
            writer.flush(out, true);
        }
        catch (IOException e) {
            e.printStackTrace();
        }
        finally {
            // 关闭writer，释放内存
            writer.close();
            //此处记得关闭输出Servlet流
            IoUtil.close(out);
        }
        return R.ok();
    }

    @Override
    public void uploadExcel(MultipartFile file) {
        try {
            ExcelReader reader = ExcelUtil.getReader(file.getInputStream());
            reader.addHeaderAlias("姓名","name");
            reader.addHeaderAlias("证件类型","cardType");
            reader.addHeaderAlias("证件编号", "idCard");
            reader.addHeaderAlias("乘车日期", "takeData");
            reader.addHeaderAlias("乘车时间", "takeTime");
            reader.addHeaderAlias("票号", "ticketNum");
            reader.addHeaderAlias("车次", "trainNumber");
            reader.addHeaderAlias("发站", "depart");
            reader.addHeaderAlias("到站", "arrived");
            reader.addHeaderAlias("车厢号", "carriage");
            reader.addHeaderAlias("席别", "seatType");
            reader.addHeaderAlias("座位号", "seatNum");
            reader.addHeaderAlias("票种", "ticketType");
            reader.addHeaderAlias("票价", "ticketPrice");
            reader.addHeaderAlias("售票处", "ticketOffice");
            reader.addHeaderAlias("窗口", "winNum");
            reader.addHeaderAlias("操作员", "operator");
            reader.addHeaderAlias("售票时间", "ticketTime");
            List<LieChe> lieCheList = reader.readAll(LieChe.class);
            this.saveBatch(lieCheList);

        }catch (IOException e){
            e.printStackTrace();
        }
    }

    /**
     * 获取主体所乘列车列表
     * @param lieChe 主体信息
     * @return
     */
    public List<LieChe> getMsgList(LieChe lieChe){

        wrapper.clear();
        //根据用户信息查找所乘列车详细信息
        String name = lieChe.getName();
        if (StringUtils.hasLength(name)) {
            wrapper.like(LieChe::getName,name);
        }
        String idCard = lieChe.getIdCard();
        if (StringUtils.hasLength(idCard)) {
            wrapper.eq(LieChe::getIdCard,idCard);
        }
        return lieCheMapper.selectList(wrapper);
    }

    /**
     * 获取所乘列车的所有正常售票人员
     * @param lieChe 主体所乘列车信息
     * @return
     */
    public List<LieChe> getAllList(LieChe lieChe) {
        wrapper.clear();
        if (StringUtils.hasLength(lieChe.getTrainNumber())) {
            wrapper.eq(LieChe::getTrainNumber,lieChe.getTrainNumber());
        }
        wrapper.groupBy(LieChe::getIdCard);
        return lieCheMapper.selectList(wrapper);
    }

    /**
     * 获取所乘列车同一车厢所有正常售票人员
     * @param lieChe 主体所乘列车信息
     * @return
     */
    public List<LieChe> getCarriageList(LieChe lieChe) {
        wrapper.clear();
        if (StringUtils.hasLength(lieChe.getCarriage())) {
            wrapper.eq(LieChe::getCarriage,lieChe.getCarriage());
        }
        wrapper.groupBy(LieChe::getIdCard);
        return lieCheMapper.selectList(wrapper);
    }

    /**
     * 根据当前所乘坐的列车信息获取同行人员
     * @param sheet 用户信息
     * @return 同乘人员列表
     */
    public List<LieChe> getPersonList(LieChe sheet) {
        wrapper.clear();
        //获取同一辆车次
        if (StringUtils.hasLength(sheet.getTrainNumber())) {
            wrapper.eq(LieChe::getTrainNumber,sheet.getTrainNumber());
        }
        //获取同一车厢
        if (StringUtils.hasLength(sheet.getCarriage())) {
            wrapper.eq(LieChe::getCarriage,sheet.getCarriage());
        }
        //获取前后三排座位
        // TODO: 2022/3/28 目前可能出现错误
        if (StringUtils.hasLength(sheet.getSeatNum())) {
            String trim = sheet.getSeatNum().substring(0,3);
            int a = Integer.parseInt(trim)+3;
            int b = Integer.parseInt(trim)-3;
            wrapper.le(LieChe::getSeatNum,a);
            wrapper.ge(LieChe::getSeatNum,b);
        }
        //有可能出现重复录入的筛选，但没有是否退票的信息，不能确定该成员是否真实乘坐本次列车
        wrapper.groupBy(LieChe::getIdCard);
        wrapper.orderByAsc(LieChe::getTakeTime);
        return lieCheMapper.selectList(wrapper);
    }

    /**
     * 时间转换工具
     * @param s 时间格式的字符串
     * @return 时间
     */
    public Date dateUtil(String s) {
        DateFormat dateFormat = new SimpleDateFormat("HH:mm");
        try {
            if (!"".equals(s))
                return dateFormat.parse(s);
            else
                return dateFormat.parse("00:00");
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }
    /**
     * 筛选前后三列中不满足同乘的人员
     * @param lieChe 主体信息
     * @param list 前后三列人员
     * @return 删选后的人员列表
     */
    public List<LieChe> getFilter(LieChe lieChe, List<LieChe> list){

        //获取该列车站点信息
        Map<String, String> stations = getStation(lieChe);
        List<LieChe> comP = new ArrayList<>();

        //通过迭代器获取用户途径的站点时间
        Iterator<Map.Entry<String, String>> iterator = stations.entrySet().iterator();
        Map<String, Date> map = new HashMap<>();
        while (iterator.hasNext()){
            Map.Entry<String, String> station = iterator.next();

            if (lieChe.getDepart().equals(station.getKey())) {
                while (iterator.hasNext()){
                    Map.Entry<String, String> next = iterator.next();
                    map.put("MIN",dateUtil(station.getValue()));
                    if (lieChe.getArrived().equals(next.getKey())){
                        map.put("MAX",dateUtil(next.getValue()));
                        break;
                    }
                }
                break;
            }
        }
        //遍历前后三排用户，根据上下车时间判断是否可能与主体发生密接
        for (LieChe sheet:list) {

            //获取上车时间
            Date min = dateUtil(sheet.getTakeTime());
            //获取下车时间
            Date max = dateUtil(stations.get(sheet.getArrived()));
            if (isEffectiveDate(map.get("MIN"),min,max) || isEffectiveDate(map.get("MAX"),min,max)) {
                comP.add(sheet);
            }

        }
        return comP;
    }
    /**
     * 判断当前时间是否在[startTime, endTime]区间，注意时间格式要一致
     *
     * @param nowTime 当前时间
     * @param startTime 开始时间
     * @param endTime 结束时间
     * @return
     */
    public static boolean isEffectiveDate(Date nowTime, Date startTime, Date endTime) {
        if (nowTime.getTime() == startTime.getTime()
                || nowTime.getTime() == endTime.getTime()) {
            return true;
        }

        Calendar date = Calendar.getInstance();
        date.setTime(nowTime);

        Calendar begin = Calendar.getInstance();
        begin.setTime(startTime);

        Calendar end = Calendar.getInstance();
        end.setTime(endTime);

        if (date.after(begin) && date.before(end)) {
            return true;
        } else {
            return false;
        }
    }
    /**
     * 获取该列车所经站点
     * @param sheetQuery 用户信息
     * @return 途径的站点 根据时间排序
     */
    public Map<String, String> getStation(LieChe sheetQuery){
        wrapper.clear();
        //获取同一辆车次
        if (StringUtils.hasLength(sheetQuery.getTrainNumber()))
            wrapper.eq(LieChe::getTrainNumber,sheetQuery.getTrainNumber());
        //获取该车次发车站点，以时间排序
        wrapper.select(LieChe::getDepart, LieChe::getTakeTime).groupBy(LieChe::getDepart).orderByAsc(LieChe::getTakeTime);
        ArrayList<LieChe> departList = (ArrayList<LieChe>) lieCheMapper.selectList(wrapper);
        //清除本次查询条件
        wrapper.clear();
        //获取到达站点信息，以时间排序 --》终点站没有时间显示
        wrapper.select(LieChe::getArrived).groupBy(LieChe::getArrived).orderByAsc(LieChe::getTakeTime);
        ArrayList<LieChe> arrivedList = (ArrayList<LieChe>) lieCheMapper.selectList(wrapper);

        for (LieChe sheet:departList) {
            arrStation.put(sheet.getDepart(),sheet.getTakeTime());
        }
        for (LieChe sheet:arrivedList) {
            arrStation.putIfAbsent(sheet.getArrived(), "");
        }

        return arrStation;
    }

    /**
     * 判断主体是否为盐城籍
     * @param lieChe 主体信息
     * @return
     */
    public boolean isYanCheng(LieChe lieChe){
        if(IdcardUtil.isValidCard18(lieChe.getIdCard())){
            int counter = lieCheMapper.getPersonInfoByCardId(lieChe.getIdCard());
            return counter == 1;
        }else {
            return false;
        }

    }

    /**
     * 获取同乘人员的所有信息，封装并转存xlsx
     * @param list
     * @return
     */
    public List<MiJiePerson> getPersonInfo(List<LieChe> list){
        List<MiJiePerson> miJieInfo = new ArrayList<>();
        for (LieChe li:list) {
            MiJiePerson person = new MiJiePerson();
            //获取该成员是否为盐城籍贯
            boolean isYan = isYanCheng(li);
            //获取该成员是否在盐城境内下车
            String arrived = li.getArrived();
            boolean arrYan = false;

            //使用枚举遍历盐城境内站点
            for (YcStation e : YcStation.values()) {
                if(e.getStationName().equals(arrived)){
                    arrYan = true;
                }
            }

            person.setName(li.getName()); //姓名
            if(IdcardUtil.isValidCard18(li.getIdCard())){
                if (Integer.parseInt(li.getIdCard().substring(16,17)) % 2 == 0) {
                    person.setSex("女");
                } else {
                    person.setSex("男");
                }
            }
            person.setIdCard(li.getIdCard()); //身份证号码
            person.setIsYan(isYan?"盐城籍":"非盐城籍"); //是否为盐城籍
            person.setCarriage(li.getCarriage()); //车厢号
            person.setSeatNum(li.getSeatNum()); //座位号
            person.setDepart(li.getDepart()); //乘车站
            person.setDepartTime(li.getTakeTime()); //乘车时间
            person.setArrived(li.getArrived()); //下车站
            if(arrStation.get(li.getArrived()).isEmpty()){
                person.setArrivedTime("终点站"); //抵达时间
            }else{
                person.setArrivedTime(arrStation.get(li.getArrived())); //抵达时间
            }
            person.setArrivedType(arrYan?"是":"否"); //是否在盐城境内下车
            miJieInfo.add(person);
        }

        return miJieInfo;
    }

    //获取下车站点以及人数
    public Map<String, List<MiJiePerson>> getDown(List<MiJiePerson> list){
        Map<String,List<MiJiePerson>> map = list.stream().collect(Collectors.groupingBy(MiJiePerson::getArrived));
        return map;
    }

    @Override
    public Boolean clean() {
        lieCheMapper.deleteAll();
        return null;
    }

    @Override
    public void getExample(HttpServletResponse response) {
        InputStream inputStream = LieCheService.class.getResourceAsStream("/template/example.xlsx");

        OutputStream out = null;
        try {
            response.setContentType("application/octet-stream;charset=utf-8");
            response.setHeader("Content-Disposition", "attachment;filename=" + URLUtil.encode("example.xlsx"));
            // 执行流操作
            out = response.getOutputStream();
            IOUtils.copy(inputStream, out);
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            // 关流
            IOUtils.closeQuietly(inputStream);
            IOUtils.closeQuietly(out);

        }
    }


}
